// Copyright (C) ZHENG Zhong <heavyzheng nospam-at gmail D0T com>
//
// $Id: element.hpp 318 2009-03-02 14:03:21Z heavyzheng $
//

#ifndef ZZHENG_XML_DOM_LIBXML2_ELEMENT_HPP_20080717__
#define ZZHENG_XML_DOM_LIBXML2_ELEMENT_HPP_20080717__

#include <zzheng/xml/config.hpp>
#include <zzheng/xml/dom/types.hpp>
#include <zzheng/xml/dom/basic_node_ptr.hpp>
#include <zzheng/xml/dom/basic_node_iterator.hpp>

#include <zzheng/xml/dom/libxml2/child_node.hpp>
#include <zzheng/xml/dom/libxml2/child_node_list.hpp>
#include <zzheng/xml/dom/libxml2/attribute.hpp>
#include <zzheng/xml/dom/libxml2/attribute_map.hpp>
#include <zzheng/xml/dom/libxml2/callbacks.hpp>
#include <zzheng/xml/dom/libxml2/libxml2_fwd.hpp>

#include <string>

#ifdef ZZHENG_MSVC
#  pragma warning(push)
#  pragma warning(disable: 4511 4512) // noncopyable warnings
#endif // ZZHENG_MSVC

namespace zzheng {
namespace xml {
namespace dom {
namespace libxml2_ {

    class child_node_list;
    class attribute_map;

    //! This class represents an XML element.
    class element: public child_node {

        friend void on_node_construct(xmlNode* px); // libxml2 callback function.
        friend class child_node_list;
        friend class attribute_map;

    public:

        typedef child_node_list::iterator        child_iterator;
        typedef child_node_list::const_iterator  const_child_iterator;
        typedef attribute_map::iterator          attribute_iterator;
        typedef attribute_map::const_iterator    const_attribute_iterator;

    public:

        ~element();

        ////////////////////////////////////////////////////////////////////////////////////////////
        // element information access
        //

        //! Returns the namespace URI of this element.
        //! \return the namespace URI of this element.
        std::string uri() const;

        //! Returns the namespace prefix of this element.
        //! \return the namespace prefix of this element.
        std::string prefix() const;

        //! Returns the qualified name (prefix:name) of this element.
        //! \return the qualified name of this element.
        std::string qname() const;

        //! Sets the local name of this element.
        //! \param name  the new local name to set.
        void set_name(const std::string& name);

        ////////////////////////////////////////////////////////////////////////////////////////////
        // child access
        //

        //! Returns the first child element, or null if there is no child elements.
        //! \return the first child element, or null if there is no child elements.
        basic_node_ptr<element> first_elem();

        //! Const version of first_elem().
        basic_node_ptr<const element> first_elem() const;

        //! Returns the first child element with the specified local name, or null if no child
        //! element is found.
        //! \param name  the local name of the child element.
        //! \return the first child element found, or null if no child element is found.
        basic_node_ptr<element> elem(const std::string& name);

        //! Const version of elem(const std::string& name).
        basic_node_ptr<const element> elem(const std::string& name) const;

        //! Returns the first child element with the specified local name and namespace URI, or
        //! null if no child element is found.
        //! \param name  the local name of the child element.
        //! \param uri   the namespace URI of the child element.
        //! \return the first child element found, or null if no child element is found.
        basic_node_ptr<element> elem(const std::string& name, const std::string& uri);

        //! Const version of elem(const std::string& name, const std::string& uri).
        basic_node_ptr<const element> elem(const std::string& name, const std::string& uri) const;

        //! Returns a reference to the child node list of this element.
        //! \return a reference to the child node list of this element.
        child_node_list& children();

        //! Const version of children().
        const child_node_list& children() const;

        ////////////////////////////////////////////////////////////////////////////////////////////
        // attribute access
        //

        //! Returns the value of an attribute by name.
        //! \param name  the attribute name.
        //! \return the attribute value, or an empty string if the attribute does not exist.
        std::string attr(const std::string& name) const;

        //! Returns the value of an attribute by name and namespace URI.
        //! \param name  the attribute name.
        //! \param uri   the attribute's namespace URI.
        //! \return the attribute value, or an empty string if the attribute does not exist.
        std::string attr(const std::string& name, const std::string& uri) const;

        //! Sets an attribute to this element. If the attribute with the same name already exists,
        //! its value will be updated.
        //! \param name   the attribute name.
        //! \param value  the attribute value.
        void set_attr(const std::string& name, const std::string& value);

        //! Sets an attribute to this element. If the attribute with the same name and URI already
        //! exists, its value will be updated.
        //! \param name   the attribute name.
        //! \param uri    the attribute URI.
        //! \param value  the attribute value.
        void set_attr(const std::string& name, const std::string& uri, const std::string& value);

        //! Returns a reference to the attribute map of this element.
        //! \return a reference to the attribute map of this element.
        attribute_map& attributes();

        //! Const version of attributes().
        const attribute_map& attributes() const;

        ////////////////////////////////////////////////////////////////////////////////////////////
        // namespace manipulation
        //

        //! Sets the namespace URI of this element. If the specified URI is empty, this function
        //! removes the original namespace URI (if it exists) from the element.
        //! \param uri  the new namespace URI to set.
        void set_uri(const std::string& uri);

        //! Declares a namespace on this element. The namespace URI is provided as an argument,
        //! while the namespace prefix is generated automatically.
        //!
        //! Note: in an XML document, namespace prefix is only an alias to a certain namespace URI,
        //! and different prefixes mapped to the same URI are semantically identical. Thus, to
        //! avoid prefix conflicts and ambiguity, each time a namespace URI is declared on the
        //! element, a new prefix will be generated automatically, and it is guaranteed to be
        //! unique in the document scope. Users are not allowed to choose a customized namespace
        //! prefix.
        //!
        //! \param uri     the namespace URI to declare.
        //! \return the auto-generated prefix mapped to the namespace URI.
        //! \throw bad_dom_operation  if the namespace URI is invalid.
        std::string declare_namespace(const std::string& uri);

    private:

        ////////////////////////////////////////////////////////////////////////////////////////////
        // private
        //

        explicit element(xmlNode* px);

        element* clone(bool recursive) const;

        //! Returns the first child element.
        //! \return pointer to the first child element, or null if there is no child elements.
        const element* first_elem_() const;

        //! Returns the first child element with the specified local name and namespace URI.
        //! \param name  the local name of the child element.
        //! \param uri   the namespace URI of the child element.
        //! \return pointer to the first child element found, or null if not found.
        const element* elem_(const std::string& name, const std::string& uri) const;

        //! Returns a namespace declaration for the specified URI. This function will firstly
        //! search a namespace declaration for the URI under the element's context. If the URI
        //! has not been declared, it will declare a namespace for the URI on the element.
        //! \param uri  the namespace URI.
        //! \return a libxml2 namespace for the URI.
        xmlNs* declare_namespace_(const std::string& uri);

    private:

        child_node_list children_;   //!< Child nodes.
        attribute_map   attributes_; //!< Attributes.

    }; // class element

} // namespace zzheng::xml::dom::libxml2_
} // namespace zzheng::xml::dom
} // namespace zzheng::xml
} // namespace zzheng

#ifdef ZZHENG_MSVC
#  pragma warning(pop) // noncopyable warnings
#endif // ZZHENG_MSVC


#endif // ZZHENG_XML_DOM_LIBXML2_ELEMENT_HPP_20080717__



